"""
Aspose.Slides animation engine for PowerPoint presentations.

This engine provides native PowerPoint animation support using Aspose.Slides,
enabling rich entrance, exit, emphasis, and motion path effects.
"""

import logging
from typing import Dict, List, Optional, Any
from dataclasses import dataclass
from enum import Enum

try:
    import signal
    import time
    
    # Set a timeout for the import
    def timeout_handler(signum, frame):
        raise ImportError("Aspose.Slides import timeout")
    
    # Set timeout (5 seconds)
    old_handler = signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(5)
    
    try:
        import aspose.slides as slides
        from aspose.slides.animation import EffectType, EffectSubtype, EffectTriggerType
        ASPOSE_AVAILABLE = True
    finally:
        signal.alarm(0)  # Cancel the alarm
        signal.signal(signal.SIGALRM, old_handler)  # Restore old handler
        
except (ImportError, Exception):
    ASPOSE_AVAILABLE = False
    slides = None
    EffectType = None
    EffectSubtype = None
    EffectTriggerType = None

from .base_engine import BaseAnimationEngine
from ...parser.content_types import AnimatedContent, Section
from ...utils.exceptions import MD2PPTError


class AsposeAnimationType(Enum):
    """映射MD2PPT动画类型到Aspose.Slides EffectType"""
    # 入场动画
    FADE_IN = "EffectType.FADE"
    APPEAR = "EffectType.APPEAR"
    FLY_IN = "EffectType.FLY"
    SLIDE_IN = "EffectType.FLY"  # 使用FLY实现滑入效果
    ZOOM_IN = "EffectType.ZOOM"
    BOUNCE_IN = "EffectType.BOUNCE"
    SPIRAL_IN = "EffectType.SPIRAL"
    WHEEL = "EffectType.WHEEL"
    
    # 强调动画
    PULSE = "EffectType.PULSE"
    BLINK = "EffectType.BLINK"
    GROW_SHRINK = "EffectType.GROW_SHRINK"
    SPIN = "EffectType.SPIN"
    
    # 退场动画  
    FADE_OUT = "EffectType.FADE"
    FLY_OUT = "EffectType.FLY"
    ZOOM_OUT = "EffectType.ZOOM"


@dataclass
class AnimationMapping:
    """动画映射配置"""
    md2ppt_type: str  # MD2PPT动画类型
    aspose_type: str  # Aspose EffectType
    aspose_subtype: Optional[str] = None  # Aspose EffectSubtype
    is_entrance: bool = True  # 是否为入场动画
    supports_direction: bool = False  # 是否支持方向参数
    supports_intensity: bool = False  # 是否支持强度参数


class AsposeAnimationEngine(BaseAnimationEngine):
    """Aspose.Slides动画引擎"""
    
    def __init__(self):
        if not ASPOSE_AVAILABLE:
            raise MD2PPTError(
                "Aspose.Slides not available. Install with: pip install aspose-slides-python"
            )
        
        super().__init__()
        self.logger = logging.getLogger(__name__)
        
        # 动画映射表
        self.animation_mappings = self._build_animation_mappings()
        
        # 方向映射
        self.direction_mappings = {
            "left": "EffectSubtype.FROM_LEFT",
            "right": "EffectSubtype.FROM_RIGHT", 
            "up": "EffectSubtype.FROM_TOP",
            "down": "EffectSubtype.FROM_BOTTOM",
            "top": "EffectSubtype.FROM_TOP",
            "bottom": "EffectSubtype.FROM_BOTTOM"
        }
    
    def _build_animation_mappings(self) -> Dict[str, AnimationMapping]:
        """构建动画映射表"""
        mappings = {}
        
        # 入场动画
        mappings["fadeIn"] = AnimationMapping("fadeIn", "FADE", is_entrance=True)
        mappings["appear"] = AnimationMapping("appear", "APPEAR", is_entrance=True)
        mappings["slideIn"] = AnimationMapping(
            "slideIn", "FLY", is_entrance=True, supports_direction=True
        )
        mappings["flyIn"] = AnimationMapping(
            "flyIn", "FLY", is_entrance=True, supports_direction=True
        )
        mappings["zoom"] = AnimationMapping("zoom", "ZOOM", is_entrance=True)
        mappings["bounce"] = AnimationMapping("bounce", "BOUNCE", is_entrance=True)
        mappings["spiral"] = AnimationMapping("spiral", "SPIRAL", is_entrance=True)
        mappings["wheel"] = AnimationMapping("wheel", "WHEEL", is_entrance=True)
        
        # 强调动画
        mappings["pulse"] = AnimationMapping("pulse", "PULSE", is_entrance=False)
        mappings["blink"] = AnimationMapping("blink", "BLINK", is_entrance=False)
        mappings["spin"] = AnimationMapping("spin", "SPIN", is_entrance=False)
        mappings["growShrink"] = AnimationMapping(
            "growShrink", "GROW_SHRINK", is_entrance=False, supports_intensity=True
        )
        
        # 退场动画
        mappings["fadeOut"] = AnimationMapping("fadeOut", "FADE", is_entrance=False)
        mappings["slideOut"] = AnimationMapping(
            "slideOut", "FLY", is_entrance=False, supports_direction=True
        )
        mappings["flyOut"] = AnimationMapping(
            "flyOut", "FLY", is_entrance=False, supports_direction=True
        )
        mappings["zoomOut"] = AnimationMapping("zoomOut", "ZOOM", is_entrance=False)
        
        return mappings
    
    def add_animation_to_shape(self, slide, shape, animated_content: AnimatedContent) -> bool:
        """为形状添加动画效果"""
        try:
            animation_type = animated_content.animation_type
            
            # 检查动画类型是否支持
            if animation_type not in self.animation_mappings:
                self.logger.warning(f"Unsupported animation type: {animation_type}")
                return False
            
            mapping = self.animation_mappings[animation_type]
            
            # 获取Aspose动画类型
            effect_type = getattr(EffectType, mapping.aspose_type)
            
            # 确定动画子类型
            effect_subtype = self._get_effect_subtype(mapping, animated_content)
            
            # 获取主序列
            main_sequence = slide.timeline.main_sequence
            
            # 添加动画效果
            trigger_type = EffectTriggerType.WITH_PREVIOUS if EffectTriggerType else None
            effect = main_sequence.add_effect(
                shape,
                effect_type,
                effect_subtype,
                trigger_type  # 自动播放
            )
            
            # 设置动画参数
            self._configure_animation_timing(effect, animated_content)
            
            self.logger.info(
                f"Added {animation_type} animation to shape: "
                f"duration={animated_content.duration}s, delay={animated_content.delay}s"
            )
            
            return True
            
        except Exception as e:
            self.logger.error(f"Failed to add animation: {e}")
            return False
    
    def _get_effect_subtype(self, mapping: AnimationMapping, 
                          animated_content: AnimatedContent) -> Any:
        """获取动画子类型"""
        # 如果支持方向且指定了方向
        if mapping.supports_direction and animated_content.direction:
            direction = animated_content.direction.lower()
            if direction in self.direction_mappings:
                subtype_name = self.direction_mappings[direction].split(".")[-1]
                return getattr(EffectSubtype, subtype_name)
        
        # 默认子类型 (处理Aspose未加载的情况)
        if not EffectSubtype:
            return None
            
        if mapping.aspose_type == "FLY":
            return EffectSubtype.FROM_LEFT if mapping.is_entrance else EffectSubtype.TO_RIGHT
        elif mapping.aspose_type == "ZOOM":
            return EffectSubtype.IN if mapping.is_entrance else EffectSubtype.OUT
        elif mapping.aspose_type == "FADE":
            return EffectSubtype.IN if mapping.is_entrance else EffectSubtype.OUT
        
        return EffectSubtype.NONE
    
    def _configure_animation_timing(self, effect, animated_content: AnimatedContent):
        """配置动画时间参数"""
        try:
            # 设置持续时间 (秒转毫秒)
            if hasattr(effect, 'timing') and hasattr(effect.timing, 'duration'):
                effect.timing.duration = animated_content.duration
            
            # 设置延迟
            if hasattr(effect, 'timing') and hasattr(effect.timing, 'trigger_delay_time'):
                effect.timing.trigger_delay_time = animated_content.delay
            
            # 设置缓动函数 (如果支持)
            if hasattr(effect, 'timing') and hasattr(effect.timing, 'acceleration'):
                if animated_content.easing == "ease-in":
                    effect.timing.acceleration = 0.5
                elif animated_content.easing == "ease-out":
                    effect.timing.decelerate = 0.5
                elif animated_content.easing == "ease-in-out":
                    effect.timing.acceleration = 0.25
                    effect.timing.decelerate = 0.25
                    
        except Exception as e:
            self.logger.warning(f"Failed to configure timing: {e}")
    
    def get_supported_animations(self) -> List[str]:
        """获取支持的动画类型列表"""
        return list(self.animation_mappings.keys())
    
    def is_animation_supported(self, animation_type: str) -> bool:
        """检查动画类型是否支持"""
        return animation_type in self.animation_mappings
    
    def get_animation_info(self, animation_type: str) -> Optional[AnimationMapping]:
        """获取动画信息"""
        return self.animation_mappings.get(animation_type)
    
    @classmethod
    def is_available(cls) -> bool:
        """检查Aspose.Slides是否可用"""
        return ASPOSE_AVAILABLE
    
    def validate_animation_config(self, animated_content: AnimatedContent) -> bool:
        """验证动画配置"""
        animation_type = animated_content.animation_type
        
        if not self.is_animation_supported(animation_type):
            return False
        
        mapping = self.animation_mappings[animation_type]
        
        # 检查方向参数
        if animated_content.direction and not mapping.supports_direction:
            self.logger.warning(
                f"Animation {animation_type} does not support direction parameter"
            )
        
        # 检查持续时间
        if animated_content.duration <= 0:
            self.logger.warning("Animation duration must be positive")
            return False
        
        return True
    
    # 实现基类抽象方法
    def render_animated_content(self, animated_content: AnimatedContent, 
                              context: Optional[Dict[str, Any]] = None) -> str:
        """渲染动画内容到PowerPoint格式 (返回描述信息)"""
        animation_type = animated_content.animation_type
        if animation_type in self.animation_mappings:
            mapping = self.animation_mappings[animation_type]
            return f"PowerPoint Animation: {mapping.aspose_type}"
        return f"Unsupported Animation: {animation_type}"
    
    def render_section(self, section: Section, 
                      animations: List[AnimatedContent],
                      context: Optional[Dict[str, Any]] = None) -> str:
        """渲染带动画的章节"""
        animation_info = []
        for anim in animations:
            info = self.render_animated_content(anim, context)
            animation_info.append(info)
        
        return f"Section '{section.heading}' with {len(animations)} animations: " + "; ".join(animation_info)
    
    def generate_animation_timeline(self, animations: List[AnimatedContent]) -> Dict[str, Any]:
        """生成动画时间轴"""
        timeline = {
            "total_duration": 0,
            "animations": []
        }
        
        current_time = 0
        for anim in animations:
            start_time = current_time + anim.delay
            end_time = start_time + anim.duration
            
            timeline["animations"].append({
                "type": anim.animation_type,
                "start_time": start_time,
                "end_time": end_time,
                "duration": anim.duration,
                "delay": anim.delay
            })
            
            current_time = end_time
        
        timeline["total_duration"] = current_time
        return timeline
    
    def get_supported_features(self) -> List[str]:
        """获取支持的功能列表"""
        return [
            "entrance_animations",
            "exit_animations", 
            "emphasis_animations",
            "timing_control",
            "direction_support",
            "easing_functions",
            "native_powerpoint"
        ]


# 工厂函数
def create_aspose_engine() -> Optional[AsposeAnimationEngine]:
    """创建Aspose动画引擎"""
    try:
        return AsposeAnimationEngine()
    except MD2PPTError as e:
        logging.getLogger(__name__).error(f"Failed to create Aspose engine: {e}")
        return None